#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/fcntl.h>

#define purge_cache_buffer() system("echo 3 > /proc/sys/vm/drop_caches")

#define BLOCK_SIZE (2 * 1024 * 1024)

void test_time_small_8k_file_without_cache(const char*);
void test_time_small_8k_file_with_cache(const char*);
void test_time_big_file(const char*);
void sort_time(double*,int);

int
main (int argc, char *argv[])
{
	test_time_big_file(argv[1]);
	sleep(2);
	test_time_small_8k_file_without_cache(argv[1]);
	sleep(2);
	test_time_small_8k_file_with_cache(argv[1]);

	return 0;
}

/* gcc -g -O2 -DTESTSMALLFILE test.c */
void
test_time_small_8k_file_without_cache (const char *dev)
{
	purge_cache_buffer();
	sleep(1);

	struct timeval begin, end;
	double diff, all_diff = 0, aspeed[15], avg_speed;
	int i = 0, step, pos = 0;						// loop position
	int iteration;
	char *buf = (char *)malloc(BLOCK_SIZE);
	/*long long rand1, rand2, rand3;
	srand(time(NULL));
	rand1 = (long long)rand();
	rand2 = (long long)rand();
	rand3 = (long long)rand();
	step = ((unsigned int)((rand1<<32)|(rand2<<16)|rand3)) % 1024;*/
	//printf("step: %d\n", step);
	step = 151;

	int fd = open(dev, O_RDONLY | O_NONBLOCK);
	for(;;)
	{
		// here is for the highest speed
		iteration = 0;
		gettimeofday(&begin, NULL);
		do
		{
			//imitate for small file to look track
			i = (i + step) % 10240;
			lseek(fd, i * 8192, SEEK_SET);

			iteration++;
			if (read(fd, buf, BLOCK_SIZE) <= 0)
				goto quit;
			gettimeofday(&end, NULL);
			diff = (end.tv_sec - begin.tv_sec) + (end.tv_usec - begin.tv_usec)/1000000.0;
		}while (diff < 1.0);
		aspeed[pos++] = (double)iteration * (BLOCK_SIZE / 1024.0) / diff;		//  KB/s
		all_diff += diff;
		if (all_diff >= 10.0)
			break;
	}
	close(fd);

	sort_time(aspeed, pos);
	avg_speed = 0;
	for (i = 0; i < pos; i++)
		avg_speed += aspeed[i];
	avg_speed /= pos;

	printf("Max speed: %fKB/s\n", aspeed[pos-1]);
	printf("Min speed: %fKB/s\n", aspeed[0]);
	printf("Average Speed without cache: %fKB/s\n", avg_speed);	

	free(buf);
quit:
	return;
}

void
test_time_small_8k_file_with_cache (const char *dev)
{
	struct timeval begin, end;
	double diff, all_diff = 0, aspeed[15], avg_speed;
	int i = 0, pos = 0;						// loop position
	int iteration;
	char *buf = (char *)malloc(BLOCK_SIZE);

	int fd;
	for(;;)
	{
		// here is for the highest speed
		iteration = 0;
		gettimeofday(&begin, NULL);
		do
		{
			iteration++;
			fd = open(dev, O_RDONLY | O_NONBLOCK);
			if (read(fd, buf, BLOCK_SIZE) <= 0)
				goto quit;
			close(fd);
			gettimeofday(&end, NULL);
			diff = (end.tv_sec - begin.tv_sec) + (end.tv_usec - begin.tv_usec)/1000000.0;

			//imitate for small file to look track
			//i = (i + 4) % 10240;
			//lseek(fd, (off_t)0, SEEK_SET);
		}while (diff < 1.0);
		aspeed[pos++] = (double)iteration * (BLOCK_SIZE / 1024.0) / (diff * 1024.0);		//  MB/s
		all_diff += diff;
		if (all_diff >= 10.0)
			break;
	}

	sort_time(aspeed, pos);
	avg_speed = 0;
	for (i = 0; i < pos; i++)
		avg_speed += aspeed[i];
	avg_speed /= pos;

	printf("Max speed: %fMB/s\n", aspeed[pos-1]);
	printf("Min speed: %fMB/s\n", aspeed[0]);
	printf("Average Speed with cache: %fMB/s\n", avg_speed);

	free(buf);
quit:
	return;
}

/* gcc -g -O2 test.c */
void
test_time_big_file (const char *dev)
{
	struct timeval head, begin, end;
	double diff, all_diff;
	char *buf = (char *)malloc(BLOCK_SIZE);
	int i, pos, iteration;
	double aspeed[15], avg_speed;
	
	//test without cache
	purge_cache_buffer();
	sleep(1);
	pos = 0;
	all_diff = 0;

	gettimeofday(&head, NULL);
	int fd = open(dev, O_RDONLY | O_NONBLOCK);
	for(;;)
	{
		// here is for the highest speed
		iteration = 0;
		gettimeofday(&begin, NULL);
		do
		{
			iteration++;
			if (read(fd, buf, BLOCK_SIZE) <= 0)
				goto quit;
			gettimeofday(&end, NULL);
			diff = (end.tv_sec - begin.tv_sec) + (end.tv_usec - begin.tv_usec)/1000000.0;
		}while (diff < 1.0);
		aspeed[pos++] = (double)iteration * (BLOCK_SIZE / 1024.0) / (diff * 1024.0);		//  MB/s
		all_diff += diff;
		if (all_diff >= 10.0)
			break;
	}
	close(fd);

	sort_time(aspeed, pos);
	avg_speed = 0;
	for (i = 0; i < pos; i++)
		avg_speed += aspeed[i];
	avg_speed /= pos;

	printf("Max speed: %fMB/s\n", aspeed[pos-1]);
	printf("Min speed: %fMB/s\n", aspeed[0]);
	printf("Average Speed without cache: %fMB/s\n", avg_speed);

	for (i = 0; i < pos; i++)
		aspeed[i] = 0;

	//test with cache
	pos = 0;
	all_diff = 0;
	gettimeofday(&head, NULL);
	fd = open(dev, O_RDONLY | O_NONBLOCK);
	for(;;)
	{
		// here is for the highest speed
		iteration = 0;
		gettimeofday(&begin, NULL);
		do
		{
			iteration++;
			if (read(fd, buf, BLOCK_SIZE) <= 0)
				goto quit;
			gettimeofday(&end, NULL);
			diff = (end.tv_sec - begin.tv_sec) + (end.tv_usec - begin.tv_usec)/1000000.0;
		}while (diff < 2.0);
		aspeed[pos++] = (double)iteration * (BLOCK_SIZE/1024.0) / (diff * 1024.0);		//  MB/s
		lseek(fd, (off_t)0, SEEK_SET);
		all_diff += diff;
		if (all_diff >= 10.0)
			break;
	}
	close(fd);

	sort_time(aspeed, pos);
	avg_speed = 0;
	for (i = 0; i < pos; i++)
		avg_speed += aspeed[i];
	avg_speed /= pos;

	printf("Max speed: %fMB/s\n", aspeed[pos-1]);
	printf("Min speed: %fMB/s\n", aspeed[0]);
	printf("Average Speed with cache: %fMB/s\n", avg_speed);

	free(buf);

quit:
	return;
}

/*
 * shell sort
 */
void
sort_time (double *bin, int size)
{
	int i, j, increment;
	double tmp;

	for (increment = size / 2; increment > 0; increment /= 2)
	{
		for (i = increment; i < size; i++)
		{
			tmp = bin[i];
			for (j = i; j >= increment; j -= increment)
				if (tmp < bin[j - increment])
					bin[j] = bin[j-increment];
				else
					break;
			bin[j] = tmp;
		}
	}
}